//
//  Render.m
//  whfdemo003
//
//  Created by 王洪飞 on 2024/6/13.
//

#import "Render.h"

@interface Render ()
{
    GLuint vaoId;
    GLuint width;
    GLuint height;
    GLuint framebuffer;
    GLuint texture;
    unsigned int quadVAO, quadVBO;
}
@property (nonatomic, strong) GLProgram *program;
@property (nonatomic, strong) GLProgram *screenProgram;

@end

@implementation Render

- (instancetype)initWithWidth:(GLuint)dWidth andHeight:(GLuint)dHeight
{
    self = [super init];
    if (self) {
        width = dWidth;
        height = dHeight;
        
        [self prepareData];
        [self setupProgram];
        [self prepareScreenData];
        [self setupScreenProgram];
        [self setupFramebuffer];
    }
    return self;
}

-(void)setupFramebuffer{
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    
    GLuint rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); // use a single renderbuffer object for both a depth AND stencil buffer.
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
    // now that we actually created the framebuffer and added all attachments we want to check if it is actually complete now

   
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status == GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"check framebuffer success : %x", status);
    } else {
        NSLog(@"check frame failed : %x", status);
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

-(void)prepareScreenData{
    float quadVertices[] = { // vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
        // positions   // texCoords
        -1.0f,  1.0f,  0.0f, 1.0f,
        -1.0f, -1.0f,  0.0f, 0.0f,
         1.0f, -1.0f,  1.0f, 0.0f,

        -1.0f,  1.0f,  0.0f, 1.0f,
         1.0f, -1.0f,  1.0f, 0.0f,
         1.0f,  1.0f,  1.0f, 1.0f
    };
    // screen quad VAO
  
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
}

-(void)prepareData{
    GLfloat vertices[] = {
        -0.5, -0.5, 0.0,
        0.5, -0.5, 0.0,
        0.5,0.5, 0.0,
        -0.5, 0.5, 0.0
    };
    GLubyte indices[] = {
        0,1,2, 0, 2, 3
    };
    glGenVertexArrays(1, &vaoId);
    glBindVertexArray(vaoId);
    GLuint vboId, eboId;
    glGenBuffers(1, &vboId);
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    glGenBuffers(1, &eboId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, (void *)0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

-(void)setupScreenProgram {
    NSString *vShaderStr = @""
    "#version 300 es \n"
    "layout(location = 0) in vec3 vPos;"
    "void main(){gl_Position = vec4(vPos, 1.0);}";
    
    NSString *fShaderStr = @""
    "#version 300 es \n"
    "precision mediump float;\n"
    "out vec4 fragColor; \n"
    "void main(){fragColor = vec4(1,1,0,1);}";
    self.screenProgram = [[GLProgram alloc] initWithVertexShaderString:vShaderStr fragmentShaderString:fShaderStr];
    
    if ([self.screenProgram link]) {
       
    }
}

-(void)setupProgram{
    NSString *vShaderStr = @""
    "#version 300 es \n"
    "layout(location = 0) in vec3 vPos;"
    "void main(){gl_Position = vec4(vPos, 1.0);}";
    
    NSString *fShaderStr = @""
    "#version 300 es \n"
    "precision mediump float;\n"
    "out vec4 fragColor; \n"
    "void main(){fragColor = vec4(1,1,0,1);}";
    self.program = [[GLProgram alloc] initWithVertexShaderString:vShaderStr fragmentShaderString:fShaderStr];
    
    if ([self.program link]) {
       
    }
    
    
    
}

- (void)draw{
    [self.program use];
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glBindVertexArray(vaoId);
    NSLog(@"draw frame == %d", framebuffer);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL);
    
    // 回到默认缓冲区
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClearColor(0.5, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    [self.screenProgram use];
    
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBindTexture(GL_TEXTURE_2D, texture);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    
    
    
}
@end
